package fpzhan.plane.program.connect;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import fpzhan.plane.program.compose.AloneCodeBlockCompose;
import fpzhan.plane.program.compose.ComposeContent;
import fpzhan.plane.program.constant.ComposeType;
import fpzhan.plane.program.function.ChainCodeBlockFunction;
import fpzhan.plane.program.function.CodeBlockFunction;
import fpzhan.plane.program.manager.JsonKeyManager;
import fpzhan.plane.program.proxy.CodeBlockProxy;
import fpzhan.plane.program.struct.CodeBlockStruct;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;


public class TryCatchCodeBlockCompose extends AloneCodeBlockCompose {

    private ChainCodeBlockConnect child;

    private Map<Class,ChainCodeBlockCompose> catchs=new LinkedHashMap<>();

    private ChainCodeBlockCompose finallys;

    public TryCatchCodeBlockCompose() {
        this.setComposeType(ComposeType.TRY);
    }

//    public ScopeCodeBlockCompose(ComposeContext composeContext) {
//        super(composeContext);
//    }


    public ChainCodeBlockFunction flow(CodeBlockFunction codeBlock, Map<Class,CodeBlockFunction> catchs, CodeBlockFunction finallys) {
        return param -> {
            try{
                if (child!=null){
                    Set preRun = new HashSet<>(param.keySet());
                     codeBlock.apply(param);
                    Set afterRun = new HashSet<>(param.keySet());
                    afterRun.removeAll(preRun);
                    param.remove((String[])afterRun.toArray(new String[]{}));
                }
            }catch (Exception e){
                if (catchs!=null){
                    for (Map.Entry<Class,CodeBlockFunction> one:catchs.entrySet()){
                        if (one.getKey().equals(e.getClass()) || one.getKey().isAssignableFrom(e.getClass())){
                            Set preRun = new HashSet<>(param.keySet());
                            param.put("exception",e);
                            one.getValue().apply(param);
                            Set afterRun = new HashSet<>(param.keySet());
                            afterRun.removeAll(preRun);
                            param.remove((String[])afterRun.toArray(new String[]{}));
                            break;
                        }
                    }
                }
            }finally {
                if(finallys!=null ){
                    Set preRun = new HashSet<>(param.keySet());
                    finallys.apply(param);
                    Set afterRun = new HashSet<>(param.keySet());
                    afterRun.removeAll(preRun);
                    param.remove((String[])afterRun.toArray(new String[]{}));
                }

            }

        };

    }

    @Override
    public CodeBlockFunction compose(List<CodeBlockProxy> proxys, JSONObject proxyJson, CodeBlockStruct struct, JSONObject structJson,
                                     ComposeContent composeContent) throws Exception {
        JSONArray proxyArray = new JSONArray(new ArrayList<>());
        JSONArray structArray = new JSONArray(new ArrayList<>());
        CodeBlockFunction function = child.compose(proxyArray,structArray,composeContent);
        Map<Class,CodeBlockFunction> map = new HashMap<>();
        JSONArray proxyCatchArray = new JSONArray(new ArrayList<>());
        JSONArray structCatchArray = new JSONArray(new ArrayList<>());
        for (Map.Entry<Class,ChainCodeBlockCompose> compose : catchs.entrySet()){
            JSONObject catchProxy=new JSONObject();
            JSONObject catchStruct=new JSONObject();
            ComposeContent content = new ComposeContent(compose.getKey());
            map.put(compose.getKey(),compose.getValue().compose(proxys,catchProxy,struct,catchStruct,content));
            proxyCatchArray.add(catchProxy);
            if(catchStruct!=null && catchStruct.size()>0)structCatchArray.add(catchStruct);
        }
        proxyJson.put(JsonKeyManager.getJsonKey().catchKey,proxyCatchArray);
        proxyJson.put(JsonKeyManager.getJsonKey().tryKey , proxyArray);
        if (structArray != null && structArray.size() > 0) structJson.put(JsonKeyManager.getJsonKey().tryKey, structArray);
        if (structCatchArray !=null && structCatchArray.size()>0) structJson.put(JsonKeyManager.getJsonKey().catchKey,structCatchArray);
        if(finallys!=null && !finallys.isEmpty()){
            JSONObject finallyProxy=new JSONObject();
            JSONObject finallyStruct=new JSONObject();
            CodeBlockFunction finallyCodeBlock = finallys.compose(proxys,finallyProxy,struct,finallyStruct,null);
            proxyJson.put(JsonKeyManager.getJsonKey().finallyKey,finallyProxy);
            if (finallyStruct !=null && finallyStruct.size()>0) structJson.put(JsonKeyManager.getJsonKey().finallyKey,finallyStruct);
            return super.compose(proxys, proxyJson, struct, structJson, composeContent, flow(function,map,finallyCodeBlock));
        }else {
            return super.compose(proxys, proxyJson, struct, structJson, composeContent, flow(function,map,null));
        }
    }


    protected void setChild(ChainCodeBlockConnect child) {
        this.child = child;
    }

    protected <T extends Exception> void addCatchs(Class<T> exception,ChainCodeBlockCompose compose){
        catchs.put(exception,compose);
    }

    protected void setFinallys(ChainCodeBlockCompose compose){
        this.finallys=compose;
    }
}
